/*
 * hashmap.c
 *        Generic hashmap implementation.
 *      a map for pair of key-value. key must be a null-end string, value is any type of data.
 *        cheungmine
 *      Sep. 22, 2007.  All rights reserved.
 */

#include "hashmap.h"
#include "list.h"

typedef struct _hash_map_t {
	size_t size;
	listnode_t** key;
	listnode_t** value;
} hash_map_t;

/* Hash a string, return a hash key */
static ulong hash_string(const char *s, int len) {
	ulong h = 0;
	int i = 0;
	assert(s);
	if (len < 0)
		len = (s ? (int) strlen(s) : 0);
	while (i++ < len) {
		h = 17* h + *s++;
	}
	return h;
}

static void _free_map_key(listnode_t* node) {
	listnode_t *old;
	while (node) {
		old = node;
		node = node->next;

		free(old->data);
		free(old);
	}
}

static void _free_map_value(listnode_t* node, pfcb_hmap_value_free pfunc) {
	listnode_t *old;
	while (node) {
		old = node;
		node = node->next;

		if (pfunc)
			(*pfunc)(old->data);
		free(old);
	}
}

/*=============================================================================
 Public Functions
 =============================================================================*/
/* Create before use */
void hmap_create(hash_map *hmap, int size) {
	(*hmap) = (hash_map_t*) malloc(sizeof(hash_map_t));
	(*hmap)->size = size;
	(*hmap)->key = (listnode_t**) calloc(size, sizeof(listnode_t*));
	(*hmap)->value = (listnode_t**) calloc(size, sizeof(listnode_t*));
}

/* Destroy after use */
extern void hmap_destroy(hash_map hmap, pfcb_hmap_value_free pfunc) {
	size_t i;
	for (i = 0; i < hmap->size; i++) {
		_free_map_key(hmap->key[i]);
		_free_map_value(hmap->value[i], pfunc);
	}

	free(hmap->key);
	free(hmap->value);
	free(hmap);
}

/* Insert a key-value into hash map. value is a pointer to callee-allocated memory */
void hmap_insert(hash_map hmap, const int key, void* value) {
	listnode_t *node_key, *node_val;
	ulong h;
	char *s;
	s = (char*) malloc(12);
	assert(s);
	memset(s, 0x00, 12);
	sprintf(s, "%d", key);

	node_key = list_node_create((void*) s);
	node_val = list_node_create(value);
	assert(node_key && node_val);

	h = hash_string(s, -1) % hmap->size;

	node_key->next = hmap->key[h];
	hmap->key[h] = node_key;

	node_val->next = hmap->value[h];
	hmap->value[h] = node_val;
}

/* Search a hash map for value of given key string */
void*
hmap_search(hash_map hmap, const int key) {
	char *s = (char*) malloc(12);
	assert(s);
	memset(s, 0x00, 12);
	sprintf(s, "%d", key);

	ulong h = hash_string(s, -1) % hmap->size;
	listnode_t *pk = hmap->key[h];
	listnode_t *pv = hmap->value[h];

	while (pk) {
		if (strcmp(s, pk->str) == 0) {
			free(s);
			return pv->data;
		}
		pk = pk->next;
		pv = pv->next;
	}

	free(s);
	return NULL;
}
